Skip to content

Conversation

Xmader
Copy link
Member

@Xmader Xmader commented Sep 6, 2024

String buffers memory would be moved around during a minor GC (nursery collection).

The string buffer pointer obtained by JS::Get{Latin1,TwoByte}LinearStringChars remains valid only as long as no GC happens.
Even though JS::PersistentRootedValue does keep the string buffer from being garbage-collected, it does not guarantee the buffer would not be moved elsewhere during a GC, and so invalidates the string buffer pointer.


The direction of @caleb-distributive's fix in PR #417 is correct.

Finally, it adds a GC callback that re-points all of the JSStringProxy data pointers to their associated data, since they may have moved during a GC.

However, from my testing, moving string buffers only happens during nursery collections (minor GC, hooked by JS::AddGCNurseryCollectionCallback).
The changes in PR 417 only check if the memory address of string buffers has changed during a full GC (hooked by JS_SetGCCallback).
Nursery collections can happen much more frequently than full GCs.

Also, https://bugzilla.mozilla.org/show_bug.cgi?id=1880044 might have changed the behaviour for garbage collection of string buffers. The latest SpiderMonkey commit on mozilla-central has landed changes to allocate flattened string buffers in the nursery.


This PR would solve the root cause of the following issues: (All because of the string corruption bug)

`JS::UniqueChars` will free the string buffer in its destructor, so it must be kept alive until the end of the function
String buffers memory would be moved around during a minor GC (nursery collection).

The string buffer pointer obtained by `JS::Get{Latin1,TwoByte}LinearStringChars` remains valid only as long as no GC happens.
Even though `JS::PersistentRootedValue` does keep the string buffer from being garbage-collected, it does not guarantee the buffer would not be moved elsewhere during a GC, and so invalidates the string buffer pointer.
@Xmader Xmader requested a review from zollqir September 6, 2024 06:28
Copy link
Collaborator

@zollqir zollqir left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@zollqir zollqir marked this pull request as ready for review September 6, 2024 14:06
@zollqir zollqir merged commit 2d19e25 into main Sep 6, 2024
34 checks passed
@zollqir zollqir deleted the Xmader/fix/string-corruption branch September 6, 2024 14:07
if (id.isString()) {
JS::RootedString idString(cx, id.toString());
const char *methodName = JS_EncodeStringToUTF8(cx, idString).get();
JS::UniqueChars idString = JS_EncodeStringToUTF8(cx, JS::RootedString(cx, id.toString()));
Copy link
Collaborator

@philippedistributive philippedistributive Sep 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dos this fix a heap use after free error?

Copy link
Member Author

@Xmader Xmader Sep 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it also fixes one, but not the main one that's due to the GC moving the string buffer around.
This one is about that the JS::UniqueChars obtained by JS_EncodeStringToUTF8(cx, idString) gets free-ed immediately after this line.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, as expected, not the main one

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Intermittent tests crashes on main URL string corruption from JS to Python in XHR DCPError: no transports defined
3 participants